#ifndef MULTIBOOT_H
#define MULTIBOOT_H

#include <Globals.h>
#include <ElfParser.h>

#define MULTIBOOT_FLAG_MEM     0x001
#define MULTIBOOT_FLAG_DEVICE  0x002
#define MULTIBOOT_FLAG_CMDLINE 0x004
#define MULTIBOOT_FLAG_MODS    0x008
#define MULTIBOOT_FLAG_AOUT    0x010
#define MULTIBOOT_FLAG_ELF     0x020
#define MULTIBOOT_FLAG_MMAP    0x040
#define MULTIBOOT_FLAG_CONFIG  0x080
#define MULTIBOOT_FLAG_LOADER  0x100
#define MULTIBOOT_FLAG_APM     0x200
#define MULTIBOOT_FLAG_VBE     0x400

/**
  Structure describing the multiboot header passed by GRUB
**/
struct multiboot_header
{
  u32int flags;
  
  u32int mem_lower;
  u32int mem_upper;
  
  u32int boot_device;
  
  u32int cmdline;
  
  u32int mods_count;
  u32int mods_addr;
  
  /* ELF information */
  u32int num;
  u32int size;
  u32int addr;
  u32int shndx;
  
  u32int mmap_length;
  u32int mmap_addr;
  
  u32int drives_length;
  u32int drives_addr;
  
  u32int config_table;
  
  u32int boot_loader_name;
  
  u32int apm_table;
  
  u32int vbe_control_info;
  u32int vbe_mode_info;
  u32int vbe_mode;
  u32int vbe_interface_seg;
  u32int vbe_interface_off;
  u32int vbe_interface_len;
}  __attribute__((packed));

typedef struct multiboot_header MultibootHeader;

/**
   Defines an interface to the multiboot header
**/
class Multiboot
{
public:
  Multiboot() :
    header(NULL) {}
  Multiboot(MultibootHeader *h);

  ~Multiboot();
  
  u32int getElfNumHeaders() { return header->num; }
  u32int getElfHeaderSize() { return header->size; }
  u32int getElfHeaderAddr() { return header->addr; }
  u32int getElfStringTabIndex() { return header->shndx; }
  u32int getLowerMem()      { return header->mem_lower; }
  u32int getUpperMem()      { return header->mem_upper; }
  u32int getHeaderFlags()   { return header->flags; }
  u32int getModStart()      
  {
    if (header->mods_count)
    {
      return *((u32int*)(header->mods_addr));
    }
    else
    {
      return 0;
    }
  }
  u32int getModEnd()
  {
    if (header->mods_count)
    {
      return *(u32int*)((header->mods_addr)+4);
    }
    else
    {
      return 0;
    }
  }
  
  Address getStrtabEnd()
  {
    if (strtab)
    {
      return (Address) strtab->addr+strtab->size;
    }
    else
    {
      return 0;
    }
  }
  
  Address getSymtabEnd()
  {
    if (symtab)
    {
      return (Address) symtab->addr+symtab->size;
    }
    else
    {
      return 0;
    }
  }
  
  Elf32SectionHeader *getSymtabStart()
  {
    if (symtab)
    {
      return (Elf32SectionHeader *)symtab;
    }
    else
    {
      return 0;
    }
  }
  
  Elf32SectionHeader *getStrtabStart()
  {
    if (strtab)
    {
      return (Elf32SectionHeader *)strtab;
    }
    else
    {
      return 0;
    }
  }
  
  bool isElf() { return header->flags & MULTIBOOT_FLAG_ELF; }
  bool hasMemInfo() { return header->flags & MULTIBOOT_FLAG_MEM; }

private:
  MultibootHeader *header;
  Elf32SectionHeader *strtab;
  Elf32SectionHeader *symtab;
};

#endif
